/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package net.worlds.object;

import java.applet.AudioClip;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import net.worlds.AssetManager;
import net.worlds.WorldsException;
import net.worlds.Main;
import net.worlds.ObjectList;
import net.worlds.PropChangeHandler;
import se.krka.kahlua.vm.JavaFunction;
import se.krka.kahlua.vm.LuaCallFrame;


public class Sound extends GameObject implements Runnable{
    private AudioClip audioClip;
    private URL url;
    private HttpURLConnection connection;
    public boolean isLoaded = false;
    public boolean shouldPlay = false;
    public boolean isPlaying = false;
    private javazoom.jlme.util.Player player;
    private Thread soundThread;
    private boolean running = true;
    private final List<Runnable> tasks = Collections.synchronizedList(new ArrayList<Runnable>(4));
    private byte[] mp3;
    public Sound() throws WorldsException
    {
        super("Sound");
        addPropChangeHandler("Sound", new PropChangeHandler()
        {

            @Override
            public void handle(Object oldValue, final Object value) throws WorldsException {
                isLoaded = false;
                if(audioClip != null || player != null)
                    stop();
                addTask(new Runnable()
                {
                    public void run() {
                        try
                        {
                            load((String) value);
                            isLoaded = true;
                            if(shouldPlay && !value.equals(ObjectList.getPropDef("Sound", "Sound").defaultValue))
                            {
                                play();
                                shouldPlay = false;
                            } 
                        } catch (Exception ex) {
                            audioClip = null;
                            player = null;
                            //if(Main.debug)
                                ex.printStackTrace();
                        }
                    }
                });
            }
        });
        addFunction("play", new JavaFunction()
        {
            public int call(LuaCallFrame callFrame, int nArguments) {
                try {
                    play();
                } catch (Exception ex) {
                    ex.printStackTrace();
                    throw new RuntimeException(ex);
                }
                return 0;
            }
        });
        addFunction("stop", new JavaFunction()
        {

            public int call(LuaCallFrame callFrame, int nArguments) {
                stop();
                return 0;
            }

        });
        //x_;
        soundThread = new Thread(this, "Sound_"+getID());
        soundThread.start();
    }
    private void load(String url) throws Exception
    {
        if(url.endsWith(".mp3"))
        {
            audioClip = null;
            this.url = new URL(url);
            try {
                URLConnection conn = Sound.this.url.openConnection();
                int len = conn.getContentLength();
                BufferedInputStream in = new BufferedInputStream(conn.getInputStream(), 2048);
                mp3 = new byte[len];
                for(int i = 0; i < len; i++)
                {
                    mp3[i] = (byte) in.read();
                }
            } catch (IOException ex) {
                Main.log("Invalid sound URL");
            }
        }
        else
        {
            player = null;
            audioClip = AssetManager.loadSound(url, url);
        }
    }
    public void play() throws WorldsException
    {
        if(Main.isServer())
            Main.getServer().sendSoundPlay(this);
        else if(isLoaded && (audioClip != null || url != null))
        {
            if(audioClip != null)
            {
                if(getBooleanProp("Loop"))
                    audioClip.loop();
                else
                    audioClip.play();
            }
            else
            {
                if(isPlaying)
                    stop();
                final Runnable r = new Runnable()
                {
                    private Runnable instance;
                    public void run() {
                        instance = this;
                        try
                        {
                            isPlaying = true;
                            
                            url = new URL(url.toString());
                            connection = (HttpURLConnection) url.openConnection();
                            InputStream in = new ByteArrayInputStream(mp3);
                            player = new javazoom.jlme.util.Player(in);
                            player.play();
                            
                            boolean stopped = player.stopped;
                            
                            connection.disconnect();
                            connection = null;
                            player = null;
                            
                            if(getBooleanProp("Loop") && !stopped)
                                Main.getScriptContext().run(new Runnable()
                                {
                                    public void run() {
                                        addTask(instance);
                                    }   
                                });
                            isPlaying = false;
                        }
                        catch(Exception ex)
                        {
                            ex.printStackTrace();
                            isPlaying = false;
                        }
                    }                    
                };
                if(Thread.currentThread() != soundThread)
                    addTask(r);
                else
                    r.run();
            }
        }
        else
        {
            shouldPlay = true;
        }
    }
    public void stop()
    {
        if(Main.isServer())
            Main.getServer().sendSoundStop(this);
        else if(audioClip != null)
        {
            audioClip.stop();
        }
        else if(player != null)
        {
            player.stop();
            
        }
    }
    @Override
    public void destructor() throws WorldsException
    {
        super.destructor();
        stop();
        addTask(new Runnable()
        {
            public void run() {
                running = false;
            }
        });
    }
    private void addTask(Runnable r)
    {
        synchronized(tasks)
        {
            tasks.add(r);
            tasks.notify();
        }
    }
    public void run() {
        while(running)
        {
            synchronized(tasks)
            {
                Iterator<Runnable> itr = tasks.iterator();
                while(itr.hasNext())
                {
                    itr.next().run();
                    itr.remove();
                }
                try {
                    tasks.wait();
                } catch (InterruptedException ex) {
                }
            }
        }
    }
}
